/*******************************************************************************
 * Copyright 2009 Dop Sun
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
/**
 * Place holder for copyright, needs to be replaced before release.
 */
package com.softao.ming.gde.internal.ui.preferences;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Text;

import com.softao.ming.gde.internal.ui.preferences.InstalledGwt.GwtDefinition;

/**
 * 
 */
public class InstalledGwtFieldEditor extends FieldEditor {
	private static class GwtNameDialog extends TitleAreaDialog {
		private final GwtDefinition mGwtToEdit;
		private final List<GwtDefinition> mExistingGwt;
		private GwtDefinition mInputData;
		
		private Text txtDirectory;
		private Text txtName;
		
		public GwtNameDialog(Shell parent, GwtDefinition gwtToEdit,
				List<GwtDefinition> existingGwt) {
			super(parent);
			
			mGwtToEdit = gwtToEdit;
			mExistingGwt = existingGwt;
		}

		@Override
		protected Control createContents(Composite parent) {
			Composite composite = (Composite) super.createContents(parent);
			if (mGwtToEdit == null) {
				getShell().setText("Add new installed GWT");
			} else {
				getShell().setText("Edit installed GWT");
			}
			
			this.setTitle("GWT Definition");
			this.setMessage("Enter the home directory of GWT.");
			return composite;
		}
		
		@Override
		protected Control createDialogArea(Composite parent) {
			Composite composite = (Composite) super.createDialogArea(parent);
			Composite client = new Composite(composite, SWT.NONE);
			GridLayout layout = new GridLayout();
			layout.numColumns = 3;
			client.setLayout(layout);
			GridData layoutData = new GridData(GridData.FILL_BOTH);
			client.setLayoutData(layoutData);
			
			Label lblDirectory = new Label(client, SWT.NONE);
			lblDirectory.setText("GWT &Home:");
			
			txtDirectory = new Text(client, SWT.BORDER);
			layoutData = new GridData(GridData.FILL_HORIZONTAL);
			txtDirectory.setLayoutData(layoutData);
			
			Button btnDirectory = new Button(client, SWT.PUSH);
			btnDirectory.setText("&Directories...");
			btnDirectory.addSelectionListener(new SelectionListener() {
				@Override
				public void widgetDefaultSelected(SelectionEvent e) {
					selectDirectory();
				}

				@Override
				public void widgetSelected(SelectionEvent e) {
					selectDirectory();
				}
			});
			
			Label lblName = new Label(client, SWT.NONE);
			lblName.setText("GWT &Name:");
			
			txtName = new Text(client, SWT.BORDER);
			layoutData = new GridData(GridData.FILL_HORIZONTAL);
			layoutData.horizontalSpan = 2;
			txtName.setLayoutData(layoutData);
			
			if (this.mGwtToEdit != null) {
				this.txtDirectory.setText(this.mGwtToEdit.getPath());
				this.txtName.setText(this.mGwtToEdit.getName());
			}
			return composite;
		}
		
		private void selectDirectory() {
	        DirectoryDialog fileDialog = new DirectoryDialog(getShell(),
					SWT.OPEN);
	        String path = this.txtDirectory.getText();
	        if (path.trim().length() != 0) {
	        	fileDialog.setFilterPath(path);
	        }
			String dir = fileDialog.open();
			if (dir != null) {
				dir = dir.trim();
				if (dir.length() > 0) {
					if (validateGWTDirectory(dir)) {
						this.setErrorMessage(null);
						
						txtDirectory.setText(dir);
					} else {
						this.setErrorMessage("Directory selected does not contain valid GWT installation.");
					}
				}
			}
		}
		
		private static final String[] GWT_FILES_TO_TEST = new String[] {
			"applicationCreator.cmd",
			"benchmarkViewer.cmd",
			"gwt-benchmark-viewer.jar",
			"gwt-dev-windows.jar",
			"gwt-ll.dll",
			"gwt-module.dtd",
			"gwt-servlet.jar",
			"gwt-user.jar",
			"i18nCreator.cmd",
			"junitCreator.cmd",
			"projectCreator.cmd"			
		};
		
		private boolean validateGWTDirectory(final String path) {
			for (String name : GWT_FILES_TO_TEST) {
				File testFile = new File(path + "/" + name);
				if (!testFile.exists()) {
					return false;
				}
			}
			
			return true;
		}
		
		@Override
		protected void okPressed() {
			if (validateInput()) {
				if (this.mGwtToEdit != null) {
					this.mGwtToEdit.setName(this.txtName.getText().trim());
					this.mGwtToEdit.setPath(this.txtDirectory.getText().trim());
				} else {
					mInputData = new GwtDefinition();
					mInputData.setDefault(false);
					mInputData.setPath(this.txtDirectory.getText().trim());
					mInputData.setName(this.txtName.getText().trim());
					mInputData.setType("User");
				}
				
				super.okPressed();
			}
		}
		
		private boolean validateInput() {
			this.setErrorMessage(null);
			
			String path = this.txtDirectory.getText().trim();
			String name = this.txtName.getText().trim();
			
			if (path.length() == 0) {
				this.setMessage("Enter the home directory of GWT.", IMessageProvider.WARNING);
				return false;
			}
			
			if (name.length() == 0) {
				this.setMessage("Enter the name of GWT.", IMessageProvider.WARNING);
				return false;
			}
			
			if (!validateGWTDirectory(path)) {
				this.setErrorMessage("Directory selected does not contain valid GWT installation.");
				return false;
			}
			
			for (GwtDefinition GwtDefinition : this.mExistingGwt) {
				if (mGwtToEdit != null) {
					if (!mGwtToEdit.getPath().equals(path) && GwtDefinition.getPath().equals(path)) {
						this.setMessage("Specified GWT has been installed.", IMessageProvider.WARNING);
						return false;
					}
					if (!mGwtToEdit.getName().equals(name) && GwtDefinition.getName().equals(name)) {
						this.setMessage("Specified name has been used for another installed GWT.", IMessageProvider.WARNING);
						return false;
					}
				} else {
					if (GwtDefinition.getPath().equals(path)) {
						this.setMessage("Specified GWT has been installed.", IMessageProvider.WARNING);
						return false;
					}
					if (GwtDefinition.getName().equals(name)) {
						this.setMessage("Specified name has been used for another installed GWT.", IMessageProvider.WARNING);
						return false;
					}
				}
			}
			return true;
		}
		
		private GwtDefinition getData() {
			if (this.mGwtToEdit != null) {
				this.mGwtToEdit.setName(this.txtName.getText().trim());
				this.mGwtToEdit.setPath(this.txtDirectory.getText().trim());
				return this.mGwtToEdit;
			} else {
				return this.mInputData;
			}
		}
	}

	private static class ContentProvider implements IStructuredContentProvider {
		@SuppressWarnings("unchecked")
		@Override
		public Object[] getElements(Object inputElement) {
			return ((List<GwtDefinition>)inputElement).toArray(new GwtDefinition[0]);
		}

		@Override
		public void dispose() {
		}

		@Override
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		}
	}

	private static class LabelProvider implements ITableLabelProvider {

		@Override
		public Image getColumnImage(Object element, int columnIndex) {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public String getColumnText(Object element, int columnIndex) {
			GwtDefinition GwtDefinition = (GwtDefinition) element;
			switch (columnIndex) {
			case 0:
				return GwtDefinition.getName();
			case 1:
				return GwtDefinition.getPath();
			case 2:
				return GwtDefinition.getType();
			default:
				return "";
			}
		}

		@Override
		public void addListener(ILabelProviderListener listener) {
		}

		@Override
		public void dispose() {

		}

		@Override
		public boolean isLabelProperty(Object element, String property) {
			return false;
		}

		@Override
		public void removeListener(ILabelProviderListener listener) {
		}

	}

	private final Composite mParent;
	private Table mTableControl;
	private CheckboxTableViewer mTableViewer;
	private Composite mButtonBox;

	private Button addButton;
	private Button removeButton;
	private Button editButton;

	private final List<GwtDefinition> mExistGwtDefinitions;

	/**
	 * @param parent
	 */
	public InstalledGwtFieldEditor(Composite parent) {
		mParent = parent;

		mExistGwtDefinitions = new ArrayList<GwtDefinition>();
		createControl(parent);
	}

	@Override
	protected void adjustForNumColumns(int numColumns) {
		Control control = getLabelControl();
		((GridData) control.getLayoutData()).horizontalSpan = numColumns;
		((GridData) mTableControl.getLayoutData()).horizontalSpan = numColumns - 1;
	}

	@Override
	protected void doFillIntoGrid(Composite parent, int numColumns) {
		Control control = getLabelControl(parent);
		GridData gd = new GridData();
		gd.horizontalSpan = numColumns;
		gd.verticalAlignment = GridData.BEGINNING;
		control.setLayoutData(gd);

		mTableControl = getTableControl(parent);
		gd = new GridData(GridData.FILL_BOTH);
		gd.horizontalSpan = numColumns - 1;
		mTableControl.setLayoutData(gd);
		
		mTableViewer.addDoubleClickListener(new IDoubleClickListener() {
			@Override
			public void doubleClick(DoubleClickEvent event) {
				doEditGwt();
			}
		});
		mTableViewer.addSelectionChangedListener(new ISelectionChangedListener() {

			@Override
			public void selectionChanged(SelectionChangedEvent event) {
				InstalledGwtFieldEditor.this.refreshValidState();
			}
			
		});
		
		mButtonBox = getButtonBoxControl(parent);
		gd = new GridData();
		gd.verticalAlignment = GridData.BEGINNING;
		mButtonBox.setLayoutData(gd);
	}

	@Override
	protected void doLoad() {
		if (mTableControl != null) {
			this.loadAllItems();
			this.refresh();
		}
	}

	@Override
	protected void doLoadDefault() {
		if (mTableControl != null) {
			this.mExistGwtDefinitions.clear();
			this.refresh();
		}
	}

	@Override
	public boolean isValid() {
		return true;
	}
	
	@Override
	protected void refreshValidState() {
		if (this.mTableControl != null && this.editButton != null && this.removeButton != null) {
			if (this.mTableControl.getSelectionCount() > 0) {
				this.editButton.setEnabled(true);
				this.removeButton.setEnabled(true);
			} else {
				this.editButton.setEnabled(false);
				this.removeButton.setEnabled(false);
			}
			super.refreshValidState();
		}
	}
	
	@Override
	protected void doStore() {
		this.saveAllItems(this.mExistGwtDefinitions);
	}

	@Override
	public int getNumberOfControls() {
		return 2;
	}

	private Composite getButtonBoxControl(Composite parent) {
		if (mButtonBox == null) {
			mButtonBox = new Composite(parent, SWT.NULL);
			GridLayout layout = new GridLayout();
			layout.marginWidth = 0;
			mButtonBox.setLayout(layout);
			createButtons(mButtonBox);
			mButtonBox.addDisposeListener(new DisposeListener() {
				public void widgetDisposed(DisposeEvent event) {
					addButton = null;
					removeButton = null;
					editButton = null;
					mButtonBox = null;
				}
			});

		} else {
			checkParent(mButtonBox, parent);
		}

		// selectionChanged();
		return mButtonBox;
	}

	private void createButtons(Composite box) {
		addButton = createPushButton(box, "ListEditor.add");//$NON-NLS-1$
		editButton = createPushButton(box, "&Edit");
		removeButton = createPushButton(box, "ListEditor.remove");//$NON-NLS-1$

		addButton.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				doAddGwt();
			}

			@Override
			public void widgetSelected(SelectionEvent e) {
				doAddGwt();
			}
		});
		
		editButton.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				doEditGwt();
			}

			@Override
			public void widgetSelected(SelectionEvent e) {
				doEditGwt();
			}
			
		});
		
		removeButton.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				doRemoveGwt();
			}

			@Override
			public void widgetSelected(SelectionEvent e) {
				doRemoveGwt();
			}
		});
	}

	private void doAddGwt() {
		List<GwtDefinition> allItems = this.mExistGwtDefinitions;
		GwtNameDialog dialog = new GwtNameDialog(getShell(), null, allItems);
		
		if (dialog.open() == Dialog.OK) {
			allItems.add(dialog.getData());
			this.store();
			refresh();
		}
	}
	
	private void doEditGwt() {
		int[] indices = this.mTableControl.getSelectionIndices();
		if (indices.length == 0) {
			return;
		}
		
		GwtDefinition gwtToedit = this.mExistGwtDefinitions.get(indices[0]);
		List<GwtDefinition> allItems = this.mExistGwtDefinitions;
		GwtNameDialog dialog = new GwtNameDialog(getShell(), gwtToedit, allItems);
		
		if (dialog.open() == Dialog.OK) {
			this.store();
			refresh();
		}
	}
	
	private void doRemoveGwt() {
		int[] indices = this.mTableControl.getSelectionIndices();
		this.mTableControl.remove(this.mTableControl.getSelectionIndices());
		for (int index : indices) {
			this.mExistGwtDefinitions.remove(index);
		}
		this.store();
		
		this.refresh();
	}

	private Shell getShell() {
		return mParent.getShell();
	}
	
	private Button createPushButton(Composite parent, String key) {
		Button button = new Button(parent, SWT.PUSH);
		button.setText(JFaceResources.getString(key));
		button.setFont(parent.getFont());
		GridData data = new GridData(GridData.FILL_HORIZONTAL);
		int widthHint = convertHorizontalDLUsToPixels(button,
				IDialogConstants.BUTTON_WIDTH);
		data.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT,
				SWT.DEFAULT, true).x);
		button.setLayoutData(data);
		return button;
	}

	private Table getTableControl(Composite parent) {
		if (mTableControl == null) {
			mTableControl = new Table(parent, SWT.BORDER | SWT.MULTI
					| SWT.V_SCROLL | SWT.H_SCROLL | SWT.CHECK | SWT.FULL_SELECTION);
			mTableControl.setFont(parent.getFont());
			// mTreeControl.addSelectionListener(getSelectionListener());
			mTableControl.addDisposeListener(new DisposeListener() {
				public void widgetDisposed(DisposeEvent event) {
					mTableControl = null;
				}
			});
			mTableControl.setHeaderVisible(true);
			mTableControl.setLinesVisible(true);

			mTableViewer = new CheckboxTableViewer(mTableControl);
			mTableViewer.addCheckStateListener(new ICheckStateListener() {
				@Override
				public void checkStateChanged(CheckStateChangedEvent event) {
					GwtDefinition GwtDefinition = (GwtDefinition)event.getElement();
					for (GwtDefinition item : mExistGwtDefinitions) {
						if (item.equals(GwtDefinition)) {
							item.setDefault(event.getChecked());
						} else {
							item.setDefault(false);
						}
					}
					
					InstalledGwtFieldEditor.this.store();
					InstalledGwtFieldEditor.this.refresh();
				}
			});
			
			TableViewerColumn colName = new TableViewerColumn(mTableViewer,
					SWT.NONE);
			colName.getColumn().setText("Name");
			colName.getColumn().setWidth(80);

			TableViewerColumn colPath = new TableViewerColumn(mTableViewer,
					SWT.NONE);
			colPath.getColumn().setText("Path");
			colPath.getColumn().setWidth(140);

			TableViewerColumn colType = new TableViewerColumn(mTableViewer,
					SWT.NONE);
			colType.getColumn().setText("Type");
			colType.getColumn().setWidth(80);

			mTableViewer.setContentProvider(new ContentProvider());
			mTableViewer.setLabelProvider(new LabelProvider());

			refresh();
		} else {
			checkParent(mTableControl, parent);
		}
		return mTableControl;
	}

	private void refresh() {
		this.mTableViewer.setInput(this.mExistGwtDefinitions);

		for (GwtDefinition item : mExistGwtDefinitions) {
			if (item.isDefault()) {
				this.mTableViewer.setChecked(item, true);
			} else {
				this.mTableViewer.setChecked(item, false);
			}
		}
		this.mTableViewer.refresh();
		this.refreshValidState();
	}

	private void loadAllItems() {
		mExistGwtDefinitions.clear();
		this.mExistGwtDefinitions.addAll(InstalledGwt.load());
	}
	
	private void saveAllItems(List<GwtDefinition> allItems) {
		InstalledGwt.store(allItems);
	}
}
